home *** CD-ROM | disk | FTP | other *** search
/ Amiga Collections: Topik / Topik - Disk 02 - Fonts and CLI Commands (19xx)(Topik Public Domain)(PD)[a][WB].zip / Topik - Disk 02 - Fonts and CLI Commands (19xx)(Topik Public Domain)(PD)[a][WB].adf / Source / disksalv.c < prev    next >
C/C++ Source or Header  |  1989-04-19  |  34KB  |  986 lines

  1. /* ====================================================================== */
  2.  
  3. /*                   DiskSalv
  4.  
  5.             AmigaDOS Disk Salvage Program
  6.                     by
  7.                 Dave Haynie
  8.  
  9.                   (c) April 1986
  10.  
  11.     May be copied magnetically or electronically without the permission
  12.     of the author, providing no charge is requested    for the program.  The
  13.     only fees permitted are a moderate copying charge, in the case of 
  14.     magnetic copying, or any normal BBS charges, in the case of an 
  15.     electronic download.  Any other uses may be negotiated by contacting
  16.     the author.  This program is copyrighted; portions of this program
  17.     source may be used as is in other programs with no necessary
  18.     acknowledgement, though acknowledgements are always appreciated.
  19.  
  20.     If you are very pleased with this program or what you learn from
  21.     the source code, please consider sending no more than $10.00 to
  22.     the author.  In addition, any bug reports would be greatly
  23.     appreciated.  All correspondence may be sent to:
  24.  
  25.         Dave Haynie
  26.         645 Allen Avenue
  27.         Gibbstown, NJ 08027
  28.  
  29.         Usenet:        {allegra,caip,seismo}!cbmvax!daveh
  30.         Compuserve:    76703,2047
  31.         QuantumLink:    Animal128
  32.  
  33.     These addresses are current as of this writing, most are likely to
  34.     remain so for awhile.
  35.     
  36.     ANYWAY, this program is designed to let the user salvage all or parts
  37.     of a damaged AmigaDOS disk.  It is used similarly to DISKCOPY or 
  38.     COPY, with the format:
  39.  
  40.         DISKSALV dfN: [TO] dfM:
  41.  
  42.     Where the source disk is the corrupted disk, the target disk is
  43.     a formatted AmigaDOS disk, usually empty.  This command 
  44.     currently takes two drives; i.e. it won't salvage df0: to df0:.
  45.  
  46.     This program salvages the bad disk by by-passing the normal
  47.     disk root directory, which may be corrupted (and as well is
  48.     probably the most likely cause of a disk being rejected by
  49.     AmigaDOS).  It performs a scan to collect all directories and
  50.     their associated files, in pointer form.  Then the directories
  51.     are created one-by-one through normal DOS calls, then the files
  52.     are read and placed in the directories.  There is no necessary 
  53.     correlation between the physical location of files on the bad
  54.     disk and the physical location of the corresponding files on
  55.     the newly created disk.  Once all linkable files are restored,
  56.         a directory of existing but unused data blocks is created, in an
  57.     effort to preserve anything left, which might be manually 
  58.     re-created.  This program will also copy a good    disk, though 
  59.     inefficiently as compared to a straight DiskCopy.  The program
  60.     will prompt for a new disk if it is having trouble writing to
  61.     the given recovery disk at any time.
  62. */
  63.  
  64. #include <exec/types.h>
  65. #include <exec/exec.h>
  66. #include <devices/trackdisk.h>
  67. #include <libraries/dos.h>
  68. #include <libraries/dosextens.h>
  69. #include <lattice/ctype.h>
  70. #include <lattice/stdio.h>
  71.  
  72. /* ====================================================================== */
  73.  
  74. /* Global definitions. */
  75.  
  76. char prog_version[] = "1.0";
  77.  
  78. #define BLOCKSIZE    TD_SECTOR
  79. #define WORDSIZE    sizeof(ULONG)
  80. #define DISK_SIZE    (NUMHEADS*NUMCYLS*NUMSECS)
  81. #define SIZE        (BLOCKSIZE / WORDSIZE)
  82. #define LIST_SIZE    (SIZE - (50+6))
  83. #define READOK        0
  84. #define BSTR_MAX    31
  85. #define PATH_MAX    255
  86.  
  87. #define FT_SHORT_FILE    2        /* Short file type */
  88. #define FT_DATA_BLOCK    8        /* Data block type */
  89. #define ST_FILE        0xfffffffd    /* File subtype */
  90. #define ST_USER_DIR    2        /* User directory subtype */
  91.  
  92. struct IOExtTD *diskreq = NULL;        /* Pointer for extended disk commands */
  93. struct MsgPort *diskport = NULL;    /* Port for trackdisk replies */
  94.  
  95. LONG diskChangeCount = 0;        /* Checks for changed disk */
  96. int device = 1;                /* Default disk device number */
  97.  
  98. /* ====================================================================== */
  99.  
  100. /* Buffer access types.  Each of these types is a template of BLOCKSIZE
  101.    used to access a TRACKDISK data block's individual elements.  There are
  102.    several types of these data blocks, "root", the disk's root directory,
  103.    "directory", a user defined directory block, "file", a file header
  104.    block, "extens", a file extension block, and "data", a data block
  105.    (the real meat of what's out there on disk.  I also define a "generic"
  106.    block, which contains most of the things that you'd like to look at
  107.    in a block who's fundamental and secondary types haven't yet been
  108.    determined.  */
  109.  
  110. /* This block type contains the elements that are found in most blocks
  111.    and usually required to identify the type of the block.  Called 
  112.    as .Gen in the block union. */
  113.  
  114. struct generic_block {
  115.    ULONG blk_type, blk_key, blk_seq, blk_size, blk_link, blk_checksum;
  116.    UBYTE blk_data[BLOCKSIZE - 9*WORDSIZE];
  117.    ULONG blk_parent, blk_extension, blk_subtype;
  118. };
  119.  
  120. /* This is the block that's used to store the actual data on the disk. Its
  121.    accesses as .Dat in the block union. */
  122.  
  123. struct data_block {
  124.    ULONG blk_type, blk_key, blk_seq, blk_size, blk_next, blk_checksum;
  125.    UBYTE blk_data[BLOCKSIZE-6*WORDSIZE];
  126. };
  127.  
  128. /* This block is used as the root directory for the entire disk.  Its
  129.    accessed as .Rot in the block union. */
  130.       
  131. struct root_block {
  132.    ULONG blk_type, blk_key, blk_seq, blk_size, blk_runused, blk_checksum;
  133.    ULONG blk_hashtable[LIST_SIZE];
  134.    ULONG blk_bmflag, blk_bitmap[49-23];
  135.    ULONG blk_days, blk_mins, blk_ticks;
  136.    UBYTE blk_name[(20-7)*WORDSIZE];
  137.    ULONG blk_createdays, blk_createmins, blk_createticks;
  138.    ULONG blk_nexthash, blk_parent, blk_extension, blk_subtype;
  139. };
  140.  
  141. /* This block is a user directory block.  It is accessed as .Dir in the
  142.    block union. */
  143.  
  144. struct directory_block {
  145.    ULONG blk_type, blk_key, blk_seq, blk_dunused1[2], blk_checksum;
  146.    ULONG blk_hashtable[LIST_SIZE];
  147.    ULONG blk_spare, blk_protect, blk_dunused2;
  148.    UBYTE blk_comment[(47-23)*WORDSIZE];
  149.    ULONG blk_days, blk_mins, blk_ticks;
  150.    UBYTE blk_name[(20-4)*WORDSIZE];
  151.    ULONG blk_nexthash, blk_parent, blk_extension, blk_subtype;
  152. };
  153.  
  154. /* This block stores a file header, which contains the information used to
  155.    link together the individual elements of a file.  Its accessed as .Fil
  156.    in the block union. */
  157.  
  158. struct file_block {
  159.    ULONG blk_type, blk_key, blk_seq, blk_size, blk_firstdata, blk_checksum;
  160.    ULONG blk_datalist[LIST_SIZE];
  161.    ULONG blk_spare, blk_protect, blk_bytesize;
  162.    UBYTE blk_comment[(47-23)*WORDSIZE];
  163.    ULONG blk_days, blk_mins, blk_ticks;
  164.    UBYTE blk_name[(20-4)*WORDSIZE];
  165.    ULONG blk_nexthash, blk_parent, blk_extension, blk_subtype;
  166. };
  167.  
  168. /* This block stores a file extension block, which is only used if the file 
  169.    is so long that the main file header doesn't fil all of the data links it 
  170.    will need.  The file header can currently store data for (BLOCKSIZE-
  171.    WORDSIZE*(50+6)) = 72 data blocks, or (72*(BLOCKSIZE-6*WORDSIZE)) = 35,136 
  172.    bytes.  It is accessed as .Ext in the block union */
  173.  
  174. struct extens_block {
  175.    ULONG blk_type, blk_key, blk_seq, blk_size, blk_firstdata, blk_checksum;
  176.    ULONG blk_datalist[LIST_SIZE];
  177.    UBYTE blk_eunused[(50-4)*WORDSIZE];
  178.    ULONG blk_nexthash, blk_parent, blk_extension, blk_subtype;
  179. };
  180.  
  181. /* And this packs them all together */
  182.  
  183. union block {
  184.    struct generic_block Gen;
  185.    struct data_block Dat;
  186.    struct root_block Rot;
  187.    struct directory_block Dir;
  188.    struct file_block Fil;
  189.    struct extens_block Ext;
  190. };
  191.  
  192. union block *buf = NULL;        /* Global disk buffer pointer */
  193. struct data_block *data = NULL;        /* Global disk data block */
  194. struct extens_block *extn = NULL;    /* Global extension block */
  195.  
  196. /* ====================================================================== */
  197.  
  198. /* This section contains structures used to store located directory names
  199.    and pointers to the files to be restored below them. */
  200.  
  201. struct d_tree {
  202.    struct d_tree *d_next;    /* Next Directory on same level */
  203.    struct d_tree *d_parent;    /* Directory on upper level */
  204.    struct d_tree *d_child;    /* First Directory on lower level */
  205.    char d_name[BSTR_MAX+1];    /* Directory Name */
  206.    int d_flags;            /* Directory build flags */
  207.    ULONG d_point;        /* Directory Disk Pointer */
  208.    struct f_list *d_list;    /* Files in directory */
  209. };
  210.  
  211. struct f_list {
  212.    struct f_list *f_next;    /* Next file in directory */
  213.    ULONG f_file;        /* Disk location of file header */
  214. };
  215.  
  216. struct d_tree *root = NULL;    /* Global directory root */
  217.  
  218. /* ====================================================================== */
  219.  
  220. /* This section defines the bit-maps. One will speed up the system, making
  221.    it read error blocks only once.  The other keeps track of used directory
  222.    blocks. */
  223.  
  224. UBYTE usdmap[DISK_SIZE/8];    /* Used directory blocks. */
  225. UBYTE badmap[DISK_SIZE/8];    /* Bad directory blocks. */
  226.  
  227. /* This function zeros out a given bitmap. */
  228.  
  229. VOID zeromap(map)
  230. UBYTE map[];
  231. {
  232.    int i;
  233.  
  234.    for (i=0; i < DISK_SIZE/8; i++) map[i] = 0;
  235. }
  236.  
  237. /* This function returns TRUE if the given block number is marked
  238.    in the given bit-map. */
  239.  
  240. BOOL checkmap(map,num)
  241. UBYTE map[];
  242. ULONG num;
  243. {
  244.   return (BOOL) ((map[num>>3] & (1 << num%8)) != 0);
  245. }
  246.  
  247. /* This function sets the given block number in the bit map. */
  248.  
  249. VOID setmap(map,num)
  250. UBYTE map[];
  251. ULONG num;
  252. {
  253.    map[num>>3] |= (1 << num%8);
  254. }
  255.  
  256. /* ====================================================================== */
  257.  
  258. /* This section contains basic functions. */
  259.  
  260. /* This function converts a BSTR to a normal "C" string, returning that
  261.    C string as its value.  The passed C string pointer is assumed to
  262.    contains enough space to store the returned C string. */
  263.  
  264. char *bstr_to_cstr(cstr,bstr)
  265. char *cstr;
  266. UBYTE bstr[];
  267. {
  268.    UBYTE size,i;
  269.    char *ptr;
  270.  
  271.    ptr = cstr;
  272.    size = (bstr[0] <= BSTR_MAX) ? (bstr[0]) : (BSTR_MAX);
  273.    for (i=1;i <= size;i++) *ptr++ = bstr[i];
  274.    *ptr = '\0';
  275.    return cstr;
  276. }
  277.  
  278. /* This function converts the given directory pointer into a string
  279.    path name, returning that path name.  The "str" pointer is assumed
  280.    to point to a long enough string to hold the entire path name. */
  281.  
  282. char *strpath(str,dir)
  283. char *str;
  284. struct d_tree *dir;
  285. {
  286.    if (dir == NULL) return (char *) strcpy(str,"");
  287.    if (dir == root) return (char *) strcpy(str,":");
  288.    if (dir->d_parent != NULL) strpath(str,dir->d_parent);
  289.    if (strlen(str) != 1) strcat(str,"/");
  290.    return (char *) strcat(str,dir->d_name);
  291. }
  292.  
  293. /* This function creates an extended IO request port. */
  294.  
  295. struct IORequest *CreateExtIO(ioReplyPort,size)
  296. struct MsgPort *ioReplyPort;
  297. LONG size;
  298. {
  299.    struct IORequest *ioReq;
  300.  
  301.    if (ioReplyPort == NULL) return ((struct IORequest *) NULL);
  302.    ioReq = (struct IORequest *) AllocMem(size, MEMF_CLEAR | MEMF_PUBLIC);
  303.    if (ioReq == NULL) return ((struct IORequest *) NULL);
  304.  
  305.    ioReq->io_Message.mn_Node.ln_Type = NT_MESSAGE;
  306.    ioReq->io_Message.mn_Node.ln_Pri = 0;
  307.    ioReq->io_Message.mn_ReplyPort = ioReplyPort;
  308.    return (ioReq);
  309. }
  310.  
  311. /* This function deletes an extended IO request port. */
  312.  
  313. DeleteExtIO(ioExt,size)
  314. struct IORequest *ioExt;
  315. LONG size;
  316. {
  317.    ioExt->io_Message.mn_Node.ln_Type = 0xff;
  318.    ioExt->io_Device = (struct Device *) -1;
  319.    ioExt->io_Unit = (struct Unit *) -1;
  320.    FreeMem(ioExt,size);
  321. }
  322.  
  323. /* ====================================================================== */
  324.  
  325. /* This section contains basic disk control functions. */
  326.  
  327. /* This function turns off the motor of the selected disk drive. */
  328.  
  329. MotorOff()
  330. {
  331.    diskreq->iotd_Req.io_Length = 0;
  332.    diskreq->iotd_Req.io_Command = TD_MOTOR;
  333.    DoIO(diskreq);
  334.    return(0);
  335. }
  336.  
  337. /* This function initializes the selected disk device. */
  338.  
  339. InitDisk()
  340. {
  341.    diskreq->iotd_Req.io_Command = TD_CHANGENUM;
  342.    DoIO(diskreq);
  343.    diskChangeCount = diskreq->iotd_Req.io_Actual;
  344. }
  345.  
  346. /* This function fills the buffer "buf" with the contents of the given
  347.    sector number (0-17xx).  Returns 0 if there's no error.  It uses the
  348.    bit map functions to set bit flags if the sector contains a hard
  349.    error.  This cuts down greatly on time, due to the fact that blocks 
  350.    with hard errors take longer to read (due to retrys). */
  351.  
  352. BYTE ReadBuf(buf,sect)
  353. union block *buf;
  354. ULONG sect;
  355. {
  356.    if (sect == 0) return 1;
  357.    if (checkmap(badmap,sect)) return 1;
  358.    diskreq->iotd_Req.io_Length = BLOCKSIZE;
  359.    diskreq->iotd_Req.io_Data = (APTR) buf;
  360.    diskreq->iotd_Req.io_Command = ETD_READ;
  361.    diskreq->iotd_Count = diskChangeCount;
  362.    diskreq->iotd_Req.io_Offset = TD_SECTOR * sect;
  363.    DoIO(diskreq);
  364.    if (diskreq->iotd_Req.io_Error != 0) {
  365.       setmap(badmap,sect);
  366.       setmap(usdmap,sect);
  367.    }      
  368.    return (diskreq->iotd_Req.io_Error);
  369. }
  370.  
  371. /* ====================================================================== */
  372.  
  373. /* This section contains functions that operate on the directory tree
  374.    and file list structures. */
  375.  
  376. /* This function accepts a disk pointer for a file list link, which
  377.    it will create and initialize. */
  378.  
  379. struct f_list *Alloc_f_list(point)
  380. ULONG point;
  381. {
  382.    struct f_list *temp;
  383.  
  384.    temp = (struct f_list *) AllocMem(sizeof(struct f_list),0);
  385.    temp->f_file = point;
  386.    temp->f_next = NULL;
  387.    return temp;
  388. }
  389.  
  390. /* This function will deallocate a complete file pointer list. */
  391.  
  392. VOID Free_f_list(list)
  393. struct f_list *list;
  394. {
  395.    if (list == NULL) return;
  396.    if (list->f_next != NULL) Free_f_list(list->f_next);
  397.    FreeMem(list,sizeof(struct f_list));
  398. }
  399.  
  400. /* This function accepts a BSTR pointer, for which it will create a 
  401.    directory entry and initialize the name string from the BSTR.  */
  402.  
  403. struct d_tree *Alloc_d_tree(point,bstr)
  404. ULONG point;
  405. UBYTE bstr[];
  406. {
  407.    struct d_tree *newtree;
  408.  
  409.    newtree = (struct d_tree *) AllocMem(sizeof(struct d_tree),0);
  410.    newtree->d_flags = 0;
  411.    newtree->d_next = NULL;
  412.    newtree->d_parent = NULL;
  413.    newtree->d_child = NULL;
  414.    newtree->d_point = point;
  415.    newtree->d_list = NULL;
  416.    bstr_to_cstr(newtree->d_name,bstr);
  417.    return newtree;
  418. }
  419.  
  420. /* This function deallocates an entire d_tree. */
  421.  
  422. Free_d_tree(tree)
  423. struct d_tree *tree;
  424. {
  425.    if (tree->d_next != NULL) Free_d_tree(tree->d_next);
  426.    if (tree->d_child != NULL) Free_d_tree(tree->d_child);
  427.    if (tree->d_list != NULL) Free_f_list(tree->d_list);
  428.    FreeMem(tree, sizeof(struct d_tree));
  429. }
  430.  
  431. /* This function returns either the tree containing 'point' within 'tree',
  432.    or NULL if 'point' does not yet exist. */
  433.  
  434. struct d_tree *Find_d_tree(tree,point)
  435. struct d_tree *tree;
  436. ULONG point;
  437. {
  438.    struct d_tree *temp;
  439.  
  440.    if (tree->d_point == point) return tree;
  441.    if (tree->d_next != NULL) 
  442.       if ((temp = Find_d_tree(tree->d_next,point)) != NULL) return temp;
  443.    if (tree->d_child != NULL)
  444.       if ((temp = Find_d_tree(tree->d_child,point)) != NULL) return temp;
  445.    return NULL;
  446. }
  447.  
  448. /* This function adds the given tree to the child list of the given
  449.    parent tree. */
  450.  
  451. Add_Next_d_tree(parent, child)
  452. struct d_tree *parent, *child;
  453. {
  454.    struct d_tree *temp;
  455.  
  456.    temp = parent->d_child;
  457.    parent->d_child = child;
  458.    child->d_next = temp;
  459.    child->d_parent = parent;
  460. }
  461.  
  462. /* This function will seek out a file, returning a pointer to its link
  463.    structure if available, NULL otherwise.  It looks for the file in the
  464.    given directory structure, and only at that level. */
  465.  
  466. struct f_list *Find_f_list(tree,point)
  467. struct d_tree *tree;
  468. ULONG point;
  469. {
  470.    struct f_list *pnt;
  471.  
  472.    for(pnt = tree->d_list; pnt != NULL; pnt = pnt->f_next)
  473.       if (pnt->f_file == point) break;
  474.    return pnt;
  475. }
  476.  
  477. /* This function links the file list "list" into the given tree's file
  478.    list. */
  479.  
  480. Add_Next_f_list(tree,list)
  481. struct d_tree *tree;
  482. struct f_list *list;
  483. {
  484.    struct f_list *temp;
  485.  
  486.    temp = tree->d_list;
  487.    tree->d_list = list;
  488.    list->f_next = temp;
  489. }
  490.  
  491. /* ====================================================================== */
  492.  
  493. /* This function accepts a disk pointer, which is assumed to be the disk
  494.    address of a directory structure that doesn't yet exist.  This function
  495.    will return a pointer to that directory after finding it on disk and
  496.    linking it into the directory tree.  If the directory can't be
  497.    found on disk it will return the root directory.  The effect of this
  498.    function is a recursive lookahead up the path of the requested 
  499.    directory, which is the initial scanning process that moves the disk head
  500.    in a non-linear fashion. */
  501.  
  502. struct d_tree *MakeParent(dp)
  503. ULONG dp;
  504. {
  505.    union block *tbuf;        /* Temporary Disk Buffer */
  506.    struct d_tree *tree;        /* Requested directory */
  507.    struct d_tree *dad;        /* Its parent directory */
  508.  
  509.    tbuf = (union block *) AllocMem(BLOCKSIZE,MEMF_CHIP);
  510.    if (ReadBuf(tbuf,dp) != READOK) {
  511.       FreeMem(tbuf,BLOCKSIZE);
  512.       return root;
  513.    }
  514.    if (tbuf->Gen.blk_type != FT_SHORT_FILE 
  515.       || tbuf->Gen.blk_subtype != ST_USER_DIR) {
  516.       FreeMem(tbuf,BLOCKSIZE);
  517.       return root;
  518.    }
  519.    if (tbuf->Dir.blk_name[0] == 0 || tbuf->Dir.blk_name[1] == 0) {
  520.       FreeMem(tbuf,BLOCKSIZE);
  521.       return root;
  522.    }
  523.    if ((dad = Find_d_tree(root,tbuf->Dir.blk_parent)) == NULL)
  524.       dad = MakeParent(tbuf->Dir.blk_parent);
  525.    tree = Alloc_d_tree(dp,tbuf->Dir.blk_name);
  526.    Add_Next_d_tree(dad,tree);
  527.    FreeMem(tbuf,BLOCKSIZE);
  528.    return tree;
  529. }
  530.  
  531. /* This function builds the directory list.  The top node is kind of a
  532.    default place, where any file who's directory is corrupted, as well 
  533.    as any files directly in the ROOT node, will be placed.  This routine
  534.    uses the global disk buffer "buf", so exercise caution when making
  535.    any modifications not to recursively overwrite this buffer.  This 
  536.    routine will tag any non-data block as "used" in the "usdmap" bitmap.
  537.    Only actual data blocks remain untagged beyond this routine. */
  538.  
  539. BuildDirTree()
  540. {
  541.    ULONG sect;                /* Current block */
  542.    struct d_tree *dir, *sub;        /* Working directory trees */
  543.    struct f_list *fil;            /* Working file list */
  544.    char str[PATH_MAX];            /* Temporary string */
  545.  
  546.    for (sect = 1; sect < DISK_SIZE; sect++) {
  547.       if (ReadBuf(buf,sect) != READOK) {
  548.          printf(" \233\113\2337mBlock %4d (ERROR)\2330m\n",sect);
  549.          continue;
  550.       }
  551.       switch (buf->Gen.blk_type) {        /* Primary Type */
  552.       case FT_SHORT_FILE : 
  553.          setmap(usdmap,sect);
  554.          switch (buf->Gen.blk_subtype) {        /* Secondary Type */
  555.          case ST_FILE :
  556.             printf(" \233\113Block %4d (FILE) Name \"",sect);
  557.             if ((dir = Find_d_tree(root,buf->Fil.blk_parent)) == NULL)
  558.                dir = MakeParent(buf->Fil.blk_parent);
  559.             printf("%s",strpath(str,dir));
  560.             if (str[strlen(str)-1] != ':') printf("/");
  561.             printf("%s",bstr_to_cstr(str,buf->Fil.blk_name));
  562.             if (sect != buf->Fil.blk_key) printf("\" Block Key Mismatch\n");
  563.             else printf("\"\n");
  564.             if (Find_f_list(dir,sect) != NULL) break;
  565.             fil = Alloc_f_list(sect);
  566.             Add_Next_f_list(dir,fil);
  567.             break;
  568.          case ST_USER_DIR : 
  569.             printf(" \233\113Block %4d (DIR)  Name \"",sect);
  570.             if ((dir = Find_d_tree(root,buf->Dir.blk_parent)) == NULL)
  571.                dir = MakeParent(buf->Dir.blk_parent);
  572.             if ((sub = Find_d_tree(dir,sect)) != NULL) {
  573.                printf("%s\n",strpath(str,sub)); break;
  574.             }
  575.             sub = Alloc_d_tree(sect,buf->Dir.blk_name);
  576.             Add_Next_d_tree(dir,sub);
  577.             printf("%s",strpath(str,sub));
  578.             if (sect != buf->Dir.blk_key) printf("\" Block Key Mismatch\n");
  579.             else printf("\"\n");
  580.             break;
  581.          default : 
  582.             printf(" \233\113Block %4d (???)\n\233\101",sect);
  583.          }
  584.          break;
  585.       case FT_DATA_BLOCK :
  586.          if (buf->Dat.blk_size == 0) setmap(usdmap,sect);
  587.          printf(" \233\113Block %4d (DATA)\n\233\101",sect); break;
  588.       default :
  589.          setmap(usdmap,sect);
  590.          printf(" \233\113Block %4d (UNUSED)\n\233\101",sect); break;
  591.       }
  592.    }
  593. }
  594.  
  595. /* ====================================================================== */
  596.  
  597. /* This section contains routines concerned with actually restoring the
  598.    file structure from the internal directory path structure. */
  599.  
  600. /* This function resolves the order of likelihood of the two normal
  601.    block links, returning as functional value the best guess.  It will 
  602.    each check block on disk, and rule it out if either it specifies a 
  603.    disk location out of range or if its place on the disk is completely
  604.    destroyed.  There are two guesses for any unresolved block address, one 
  605.    for the data block link pointer, one for the file block header key 
  606.    pointer.  Its only necessary to call this routine if the two values are 
  607.    different, i.e., at least one of the pointers has been messed up.  This
  608.    now also checks the "used" bit-map to avoid repeated/circular files being
  609.    created. */
  610.  
  611. ULONG FindPoints(g1,g2,seq,head)
  612. ULONG g1,g2,seq,head;
  613. {
  614.    struct data_block *blk;
  615.    UBYTE q1=0,q2=0;
  616.  
  617.    printf("   \2337mResolving link conflict %d <-> %d\2330m\n",g1,g2);
  618.    if (g1 > DISK_SIZE) g1 = 0; if (g2 > DISK_SIZE) g2 = 0;
  619.    if (checkmap(usdmap,g1)) g1=0;
  620.    if (checkmap(usdmap,g2)) g2=0;
  621.    if (g1 == 0) return g2; if (g2 == 0) return g1;
  622.    blk = (struct data_block *) AllocMem(BLOCKSIZE,MEMF_CHIP);
  623.    if (ReadBuf(blk,g1) == READOK)
  624.       q1 = TRUE + (blk->blk_seq == seq) + (blk->blk_key == head) +
  625.            (blk->blk_type == FT_DATA_BLOCK);
  626.    if (ReadBuf(blk,g2) == READOK)
  627.       q2 = TRUE + (blk->blk_seq == seq) + (blk->blk_key == head) +
  628.            (blk->blk_type == FT_DATA_BLOCK);
  629.    FreeMem(blk,BLOCKSIZE);
  630.    if (q1 == 0) g1 = 0; if (q2 == 0) g2 = 0;
  631.    if (q1 > q2) return g1; else return g2;
  632. }
  633.  
  634. /* This function returns the next available file header data link
  635.    pointer.  If the current file header is exhausted it will open
  636.    up an extension block based on the "eptr" disk pointer. */
  637.  
  638. BOOL NewPoint(ptr,list,eptr)
  639. ULONG *ptr, **list, *eptr;
  640. {
  641.    (*ptr)--;
  642.    if (*ptr < 0) {
  643.       if (ReadBuf(extn,*eptr) != READOK) {
  644.          printf("  \2337mERROR: Bad Extension Block - No More Link Check\2330m\n");
  645.          return FALSE;
  646.       } else {
  647.          printf("  Linking via Extension Block %d\n", extn->blk_key);
  648.          *list = extn->blk_datalist;
  649.          *eptr = extn->blk_extension;
  650.          *ptr = LIST_SIZE-1;
  651.       }   
  652.    }
  653.    return TRUE;
  654. }
  655.  
  656. /* This function rebuilds a single file from the passed file buffer
  657.    information.  It uses the list of data block keys to check the
  658.    linking of the individual data blocks.  It can resolve a corruption
  659.    of either a data block chain pointer or of the data block key list.
  660.    If an extension block is required its called for here; though if the
  661.    extension block pointer has been corrupted then the rest of the
  662.    file rebuild will have to take palace without verification from a
  663.    data block key list.   This function isn't currently recursive, as 
  664.    disk buffers "data" and "ext" are global variables.  Every valid
  665.    data block found is marked in the "used" bitmap.  The function 
  666.    return TRUE if the remake works, FALSE if the remake fails. */
  667.  
  668. BOOL FileRemake(file,blk)
  669. struct FileHandle *file;
  670. struct file_block *blk;
  671. {
  672.    ULONG kptr, *klst;            /* Manages file block keys. */
  673.    ULONG link;                /* Chained link key */
  674.    ULONG eptr;                /* Next extension block key. */
  675.    ULONG seq;                /* Block sequence number */
  676.    BOOL ver=TRUE;            /* Recover verification enabled */
  677.  
  678.    kptr = LIST_SIZE - 1;
  679.    klst = blk->blk_datalist;
  680.    eptr = blk->blk_extension;
  681.    link = blk->blk_firstdata;
  682.    seq = 0;
  683.    data->blk_seq = 0;
  684.    while (link!=0 || seq<blk->blk_seq && data->blk_seq<=blk->blk_seq) {
  685.       seq++;
  686.       if (link != klst[kptr])
  687.          link = FindPoints(link,klst[kptr],seq,blk->blk_key);
  688.       if (link == 0) break;
  689.       if (ver) ver = NewPoint(&kptr,&klst,&eptr);
  690.       if (ReadBuf(data,link) == READOK) {
  691.          setmap(usdmap,link);
  692.          if (Write(file,data->blk_data,min(BLOCKSIZE-6*WORDSIZE,
  693.              data->blk_size)) == -1) {
  694.             if (IoErr() == ERROR_DISK_FULL) return FALSE;
  695.          }
  696.          link = data->blk_next;
  697.       } else if (ver) {
  698.          printf("  \2337mERROR: Disk Fault -- File may be incomplete\2330m\n");
  699.          link = klst[kptr];
  700.       } else {
  701.          printf("  \2337mERROR: Double Disk Fault -- File truncated\n\2330m");
  702.          break;
  703.       }
  704.    }            
  705.    return TRUE;
  706. }
  707.  
  708. /* This function accepts a string name.  It creates a unique directory
  709.    under that name, if possible.  If it fails after a reasonable number
  710.    of attempts, it returns FALSE, otherwise TRUE. */
  711.  
  712. BOOL MakeUniqueDir(name)
  713. char *name;
  714. {
  715.    struct FileLock *lock;
  716.    int len;
  717.    char addon;
  718.  
  719.    if ((lock = (struct FileLock *) CreateDir(name)) != 0) {
  720.       printf("\2333mBuilding Directory \"%s\"\2330m\n", name);
  721.       UnLock(lock);
  722.       return TRUE;
  723.    }
  724.    printf("\2337mERROR: Bad Directory Create \"%s\", retrying...\2330m\n",name);
  725.  
  726.    len = strlen(name);
  727.    name[len++] = '-';
  728.    name[len+1] = '\0';
  729.  
  730.    for (addon = '0'; addon <= '9'; addon++) {
  731.       name[len] = addon;
  732.       if ((lock = (struct FileLock *) CreateDir(name)) != 0) {
  733.          printf("\2333mBuilding Directory \"%s\"\2330m\n", name);
  734.          UnLock(lock);
  735.          return TRUE;
  736.       }
  737.    }
  738.    return FALSE;
  739.  
  740. /* This function is called when a disk is assumed to be full.  It will
  741.    prompt the user for a new disk, then create the current directory on
  742.    the new disk and exit, where the attempt to make the file take place
  743.    again.  */
  744.  
  745. VOID BackTraceDir(fil)
  746. char *fil;
  747. {
  748.    char newdsk[PATH_MAX];
  749.    int i = 0;
  750.  
  751.    while ((newdsk[i++] = *fil++) != ':'); newdsk[i] = '\0';
  752.    printf("\2337m\nDISK FULL: Insert New Disk In %s, type RETURN\2330m", newdsk);
  753.    getchar();
  754.    while (*fil) {
  755.       while ((newdsk[i++] = *fil++) != '/') if (!*fil) return;
  756.       newdsk[i-1] = '\0';
  757.       if (!MakeUniqueDir(newdsk)) return;
  758.       newdsk[i-1] = '/';
  759.    }
  760. }
  761.  
  762. /* This function rebuilds a directory tree.   It will rebuild the contents of
  763.    directory "dir" (just the files), and any siblings that "dir" may have.  It
  764.    also check each sibling for any children, and will recursively run itself 
  765.    on these children to effectively rebuild the entire directory tree.  This 
  766.    function uses the global disk buffer "buf", so be very careful when making 
  767.    any recursive modifications that this buffer dosen't get overwritten. */
  768.  
  769. VOID TreeRemake(dev,dir)
  770. char *dev;
  771. struct d_tree *dir;
  772. {
  773.    struct f_list *list;
  774.    struct d_tree *sibling;
  775.    char filespec[PATH_MAX];
  776.    UBYTE dirlen;
  777.    struct FileHandle *fh;
  778.    struct file_block *blk;
  779.    BOOL fails = FALSE;
  780.  
  781.    blk = (struct file_block *) buf;
  782.    strcpy(filespec,dev);
  783.    for (sibling = dir; sibling != NULL; sibling = sibling->d_next) {
  784.       strpath(filespec+3,sibling);
  785.       if (sibling != root) if (!MakeUniqueDir(filespec)) continue;
  786.       dirlen = strlen(filespec);
  787.       if (filespec[dirlen-1] != ':') filespec[dirlen++] = '/';
  788.       for (list = sibling->d_list; list != NULL; list = list->f_next) {
  789.          if (ReadBuf(blk,list->f_file) != READOK) continue;
  790.          bstr_to_cstr(filespec+dirlen,blk->blk_name);
  791.          do {
  792.             fails = FALSE;
  793.             if ((fh = (struct FileHandle *) Open(filespec,MODE_NEWFILE)) == 0)
  794.                printf(" \2337mERROR: Can't Create File \"%s\"\2330m\n", filespec);
  795.             else {
  796.                printf(" Recovering File \"%s\"\n", filespec);
  797.                if (blk->blk_seq!=0 || blk->blk_firstdata!=0) {
  798.                   if (!FileRemake(fh,blk)) {
  799.                      Close(fh);
  800.                      DeleteFile(filespec);
  801.                      BackTraceDir(filespec);
  802.                      fails = TRUE;
  803.                   } else 
  804.                      Close(fh);
  805.                }
  806.             }
  807.          } while (fails);
  808.       }
  809.       if (sibling->d_child != NULL) TreeRemake(dev,sibling->d_child);
  810.    }
  811. }
  812.  
  813. /* ====================================================================== */
  814.  
  815. /* This function salvages any loose blocks found floating around on the
  816.    disk.  They must be data blocks, and they must not be empty.  The names
  817.    of the blocks as saved are "BLK-####-####-##", where the first field
  818.    is the block number found, the second field the indicated parent, and
  819.    the third field the file's sequence number.  They're stored in a
  820.    directory named SPARE-BLOCKS##.  The ## field is ususally 00, but
  821.    if a SPARE-BLOCKS file already exists, this will be incremented to keep
  822.    the directories unique. */
  823.  
  824. VOID LooseSalv(dev)
  825. char *dev;
  826. {
  827.    int i;
  828.    BOOL clear = FALSE;
  829.    char filespec[PATH_MAX];
  830.    int dirext = -1;
  831.    struct FileLock *lock;
  832.    struct FileHandle *fh;
  833.  
  834.    for (i=0; i < DISK_SIZE/8 && !clear; i++) clear = (usdmap[i] == 0);
  835.    if (clear) {
  836.       printf(" No Loose Blocks Encountered\n");
  837.       return;
  838.    }
  839.    while (TRUE) {
  840.       sprintf(filespec,"%sSPARE-BLOCKS%d",dev,++dirext);
  841.       if ((lock = (struct FileLock *) Lock(filespec,ACCESS_WRITE)) == 0)
  842.          break;
  843.       else
  844.          UnLock(lock);
  845.    }
  846.    lock = (struct FileLock *) CreateDir(filespec); UnLock(lock);
  847.    printf("\2333mCreated Directory %s\2330m\n",filespec);
  848.    for (i=0; i < DISK_SIZE; i++) 
  849.       if (!checkmap(usdmap,i)) {
  850.          ReadBuf(buf,i);
  851.          sprintf(filespec,"%sSPARE-BLOCKS%d/BLK-%d-%d-%d",
  852.                  dev,dirext,i,buf->Dat.blk_key,buf->Dat.blk_seq);
  853.          printf("  Saving %s\n",filespec);
  854.          fh = (struct FileHandle *) Open(filespec,MODE_NEWFILE);
  855.          Write(fh,buf->Dat.blk_data,
  856.                min(BLOCKSIZE-6*WORDSIZE,buf->Dat.blk_size));
  857.          Close(fh);
  858.       }
  859. }
  860.  
  861. /* ====================================================================== */
  862.  
  863. /* This function attempts to open the proper I/O devices and memory space,
  864.    then perform a disk salvage.  If at any point the disk salvage cannot
  865.    be properly done, the function returns FALSE.  If successful, it will
  866.    return TRUE.  Strings "old" and "new" are the names of the source and
  867.    destination drives.  */
  868.  
  869. BOOL do_it(old,new)
  870. char *old, *new;
  871. {
  872.    char dummy;
  873.  
  874.   /* This is the stuff that need opening... */
  875.    if ((diskport = (struct MsgPort *) CreatePort("diskreq.port",0)) == NULL) {
  876.       printf("\2337mERROR: Cannot create message port\2330m\n");
  877.       return FALSE;
  878.    }
  879.    if ((diskreq = (struct IOExtTD *) 
  880.       CreateExtIO(diskport,sizeof(struct IOExtTD))) == NULL) {
  881.       printf("\2337mERROR: Cannot create I/O port\2330m\n");
  882.       return FALSE;
  883.    }
  884.    if ((buf = (union block *)AllocMem(BLOCKSIZE,MEMF_CHIP))==NULL) 
  885.       return FALSE;
  886.    if ((data = (struct data_block *)AllocMem(BLOCKSIZE,MEMF_CHIP))==NULL)
  887.       return FALSE;
  888.    if ((extn = (struct extens_block *)AllocMem(BLOCKSIZE,MEMF_CHIP))==NULL)
  889.       return FALSE;
  890.    if (OpenDevice(TD_NAME,device,diskreq,0)) {
  891.       printf("\2337mERROR: Cannot open TrackDisk device\2330m\n");
  892.       return FALSE;
  893.    }
  894.   /* Looks like we made it.  Let's boogie. */
  895.    printf("DiskSalv %s by Dave Haynie\n\n",prog_version);
  896.    printf("Place disk to salvage FROM in drive %s\n", old);
  897.    printf("Place disk to salvage  TO  in drive %s\n", new);   
  898.    printf("Type RETURN to continue");
  899.    dummy = getchar();
  900.    InitDisk();
  901.   /* Let's clear the bitmaps */
  902.    zeromap(badmap);
  903.    zeromap(usdmap);
  904.    setmap(usdmap,0);
  905.   /* Its simple to build the directory tree from here. */
  906.    printf("\nBuilding Directory Map...\n");
  907.    root = Alloc_d_tree(880,"\001:0");
  908.    BuildDirTree();
  909.   /* And just as simple to copy all that stuff over to the new disk. */
  910.    printf("\nSalvaging Disk Structure...\n");
  911.    TreeRemake(new,root);
  912.   /* Now save any stray blocks. */
  913.    printf("\nSalvaging Loose Blocks...\n");
  914.    LooseSalv(new);
  915.   /* Finally a preliminary clean-up. */
  916.    printf("Cleaning Up...\n");
  917.    Free_d_tree(root);
  918.    return TRUE;
  919. }
  920.  
  921. /* This is the main program.  It checks to make sure that you type things
  922.    the way I like them, then it calls the do_it routine to really do stuff. */
  923.  
  924. main(argc,argv)
  925. int argc;
  926. char *argv[];
  927. {
  928.    char *in_dev, *out_dev, temp, stat;
  929.  
  930.    switch (argc) {
  931.    case 2:
  932.       if (argv[1][0] == '?') {
  933.          printf("DiskSalv %s (c)1986 by \"Hazy\" Dave Haynie\n\n", 
  934.          prog_version);
  935.          printf("  This program salvages a bad AmigaDOS disk by copying any\n");
  936.          printf("recoverable files or directories from the bad disk to a good\n");
  937.          printf("formatted disk.  It also copies loose data blocks (blocks\n");
  938.          printf("who's file header was destroyed).  The format is:\n\n");
  939.          printf("  DiskSalv dfM: [TO] dfN:\n\n");
  940.          printf("where N and M are different 3 1/2\" drives from 0 to 3.\n");
  941.          printf("  This program may be distributed free for any personal\n");
  942.          printf("usage without additional permission.  If you find this\n");
  943.          printf("program very useful, please consider paying $10 or less for\n");
  944.          printf("its use.  You may also direct any questions, bug reports,\n");
  945.          printf("or macadamia nuts to the author at:\n");
  946.          printf("   Dave Haynie\n   645 Allen Avenue\n   Gibbstown, NJ 08027\n");
  947.          printf("   Usenet    :  {allegra,caip,seismo}!cbmvax!daveh\n");
  948.          printf("   Compuserve:  76703,2047\n   QuantumLink: Animal128\n");
  949.          exit();
  950.       }
  951.    default:
  952.       printf("Usage: DiskSalv dfM: [TO] dfN:, type \"DiskSalv ?\" for help.\n");
  953.       exit();
  954.    case 3:
  955.       in_dev = argv[1];  out_dev = argv[2];  break;
  956.    case 4:
  957.       in_dev = argv[1];  out_dev = argv[3];  break;
  958.    }
  959.    device = in_dev[2] - '0';
  960.    if (device < 0 || device > 4) {
  961.       printf("\2337mERROR: Invalid input drive specification\2330m\n");
  962.       exit();
  963.    }
  964.    temp = out_dev[2] - '0';
  965.    if (temp < 0 || temp > 4) {
  966.       printf("\2337mERROR: Invalid output drive specification\2330m\n");
  967.       exit();
  968.    }
  969.    if (temp == device) {
  970.       printf("\2337mSORRY: Only 2-drive salvage is currently supported\2330m\n");
  971.       exit();
  972.    }
  973.    stat =  do_it(in_dev,out_dev);    /* Perform the recover function. */
  974.    if (data != NULL) FreeMem(data,BLOCKSIZE);
  975.    if (extn != NULL) FreeMem(extn,BLOCKSIZE);
  976.    if (buf != NULL)  FreeMem(buf,BLOCKSIZE);
  977.    if (diskreq != NULL) {
  978.       MotorOff();
  979.       CloseDevice(diskreq);
  980.       DeleteExtIO(diskreq, sizeof(struct IOExtTD));
  981.    }
  982.    if (diskport != NULL) DeletePort(diskport);
  983.    exit(stat);
  984. }
  985.